<?php

/*
 * This file is part of SwiftMailer. (c) 2004-2009 Chris Corbyn For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
 */

/**
 * Sends Messages using the mail() function.
 *
 * It is advised that users do not use this transport if at all possible
 * since a number of plugin features cannot be used in conjunction with this
 * transport due to the internal interface in PHP itself.
 *
 * The level of error reporting with this transport is incredibly weak, again
 * due to limitations of PHP's internal mail() function. You'll get an
 * all-or-nothing result from sending.
 *
 * @package Swift
 * @subpackage Transport
 * @author Chris Corbyn
 */
class Swift_Transport_MailTransport implements Swift_Transport {
	/**
	 * Addtional parameters to pass to mail()
	 */
	private $_extraParams = '-f%s';
	
	/**
	 * The event dispatcher from the plugin API
	 */
	private $_eventDispatcher;
	
	/**
	 * An invoker that calls the mail() function
	 */
	private $_invoker;
	
	/**
	 * Create a new MailTransport with the $log.
	 * 
	 * @param Swift_Transport_Log $log        	
	 */
	public function __construct(Swift_Transport_MailInvoker $invoker, Swift_Events_EventDispatcher $eventDispatcher) {
		$this->_invoker = $invoker;
		$this->_eventDispatcher = $eventDispatcher;
	}
	
	/**
	 * Not used.
	 */
	public function isStarted() {
		return false;
	}
	
	/**
	 * Not used.
	 */
	public function start() {
	}
	
	/**
	 * Not used.
	 */
	public function stop() {
	}
	
	/**
	 * Set the additional parameters used on the mail() function.
	 *
	 * This string is formatted for sprintf() where %s is the sender address.
	 *
	 * @param string $params        	
	 * @return Swift_Transport_MailTransport
	 */
	public function setExtraParams($params) {
		$this->_extraParams = $params;
		
		return $this;
	}
	
	/**
	 * Get the additional parameters used on the mail() function.
	 *
	 * This string is formatted for sprintf() where %s is the sender address.
	 *
	 * @return string
	 */
	public function getExtraParams() {
		return $this->_extraParams;
	}
	
	/**
	 * Send the given Message.
	 *
	 * Recipient/sender data will be retrieved from the Message API.
	 * The return value is the number of recipients who were accepted for delivery.
	 *
	 * @param Swift_Mime_Message $message        	
	 * @param
	 *        	string[] &$failedRecipients to collect failures by-reference
	 * @return int
	 */
	public function send(Swift_Mime_Message $message, &$failedRecipients = null) {
		$failedRecipients = ( array ) $failedRecipients;
		
		if ($evt = $this->_eventDispatcher->createSendEvent ( $this, $message )) {
			$this->_eventDispatcher->dispatchEvent ( $evt, 'beforeSendPerformed' );
			if ($evt->bubbleCancelled ()) {
				return 0;
			}
		}
		
		$count = (count ( ( array ) $message->getTo () ) + count ( ( array ) $message->getCc () ) + count ( ( array ) $message->getBcc () ));
		
		$toHeader = $message->getHeaders ()->get ( 'To' );
		$subjectHeader = $message->getHeaders ()->get ( 'Subject' );
		
		if (! $toHeader) {
			throw new Swift_TransportException ( 'Cannot send message without a recipient' );
		}
		$to = $toHeader->getFieldBody ();
		$subject = $subjectHeader ? $subjectHeader->getFieldBody () : '';
		
		$reversePath = $this->_getReversePath ( $message );
		
		// Remove headers that would otherwise be duplicated
		$message->getHeaders ()->remove ( 'To' );
		$message->getHeaders ()->remove ( 'Subject' );
		
		$messageStr = $message->toString ();
		
		$message->getHeaders ()->set ( $toHeader );
		$message->getHeaders ()->set ( $subjectHeader );
		
		// Separate headers from body
		if (false !== $endHeaders = strpos ( $messageStr, "\r\n\r\n" )) {
			$headers = substr ( $messageStr, 0, $endHeaders ) . "\r\n"; // Keep last EOL
			$body = substr ( $messageStr, $endHeaders + 4 );
		} else {
			$headers = $messageStr . "\r\n";
			$body = '';
		}
		
		unset ( $messageStr );
		
		if ("\r\n" != PHP_EOL) {
			// Non-windows (not using SMTP)
			$headers = str_replace ( "\r\n", PHP_EOL, $headers );
			$body = str_replace ( "\r\n", PHP_EOL, $body );
		} else {
			// Windows, using SMTP
			$headers = str_replace ( "\r\n.", "\r\n..", $headers );
			$body = str_replace ( "\r\n.", "\r\n..", $body );
		}
		
		if ($this->_invoker->mail ( $to, $subject, $body, $headers, sprintf ( $this->_extraParams, $reversePath ) )) {
			if ($evt) {
				$evt->setResult ( Swift_Events_SendEvent::RESULT_SUCCESS );
				$evt->setFailedRecipients ( $failedRecipients );
				$this->_eventDispatcher->dispatchEvent ( $evt, 'sendPerformed' );
			}
		} else {
			$failedRecipients = array_merge ( $failedRecipients, array_keys ( ( array ) $message->getTo () ), array_keys ( ( array ) $message->getCc () ), array_keys ( ( array ) $message->getBcc () ) );
			
			if ($evt) {
				$evt->setResult ( Swift_Events_SendEvent::RESULT_FAILED );
				$evt->setFailedRecipients ( $failedRecipients );
				$this->_eventDispatcher->dispatchEvent ( $evt, 'sendPerformed' );
			}
			
			$message->generateId ();
			
			$count = 0;
		}
		
		return $count;
	}
	
	/**
	 * Register a plugin.
	 *
	 * @param Swift_Events_EventListener $plugin        	
	 */
	public function registerPlugin(Swift_Events_EventListener $plugin) {
		$this->_eventDispatcher->bindEventListener ( $plugin );
	}
	
	// -- Private methods
	
	/**
	 * Determine the best-use reverse path for this message
	 */
	private function _getReversePath(Swift_Mime_Message $message) {
		$return = $message->getReturnPath ();
		$sender = $message->getSender ();
		$from = $message->getFrom ();
		$path = null;
		if (! empty ( $return )) {
			$path = $return;
		} elseif (! empty ( $sender )) {
			$keys = array_keys ( $sender );
			$path = array_shift ( $keys );
		} elseif (! empty ( $from )) {
			$keys = array_keys ( $from );
			$path = array_shift ( $keys );
		}
		
		return $path;
	}
}
